package my.mall.front.api.service.pay

import com.alipay.api.AlipayApiException
import com.alipay.api.AlipayClient
import com.alipay.api.AlipayResponse
import com.alipay.api.internal.util.AlipaySignature
import com.alipay.api.internal.util.StringUtils
import com.alipay.api.request.AlipayTradeQueryRequest
import com.alipay.api.request.AlipayTradeWapPayRequest
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper
import jakarta.servlet.http.HttpServletRequest
import my.mall.common.annotation.Slf4j
import my.mall.common.annotation.Slf4j.Companion.log
import my.mall.common.bean.JsonParser
import my.mall.common.util.BigDecimalUtils.fenToYuan
import my.mall.common.util.BigDecimalUtils.yuanToFen
import my.mall.db.entity.Order
import my.mall.db.service.IOrderService
import my.mall.front.api.util.OrderUtils
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import org.springframework.util.StreamUtils
import java.io.IOException
import java.nio.charset.StandardCharsets

@Slf4j
@Service("ali")
class AliPaymentService(
    private val orderService: IOrderService,
    private val alipayClient: AlipayClient,
    private val jsonParser: JsonParser
) : PaymentService {
    private val name = "ali"

    @Value("\${alipay.notify-url}")
    private lateinit var notifyUrl: String

    @Value("\${alipay.return-url}")
    private lateinit var returnUrl: String

    @Value("\${alipay.public-key}")
    private lateinit var publicKey: String

    override fun getPaymentPage(order: Order): String {
        val request = AlipayTradeWapPayRequest()
        request.bizContent = ("{\"out_trade_no\":\"" + getOutTradeNo(order.id) + "\","
                + "\"total_amount\":\"" + fenToYuan(order.actualPrice) + "\","
                + "\"subject\":\"迷你商城\","
                + "\"product_code\":\"QUICK_WAP_WAY\"}")
        request.notifyUrl = notifyUrl
        request.returnUrl = returnUrl
        val response: AlipayResponse?
        try {
            response = alipayClient.pageExecute(request)
            if (response != null && response.isSuccess()) {
                return response.getBody()
            }
        } catch (e: AlipayApiException) {
            log.warn("get payment info failed", e)
        }
        return ""
    }

    override fun checkType(request: HttpServletRequest): Boolean {
        val tradeNo = request.getParameter("out_trade_no")
        return if (StringUtils.isEmpty(tradeNo)) {
            false
        } else tradeNo.startsWith(name)
    }

    override fun callback(request: HttpServletRequest) {
        val outTradeNo = request.getParameter("out_trade_no")
        if (StringUtils.isEmpty(outTradeNo)) {
            return
        }
        val orderNoArr = outTradeNo.split("-".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
        if (orderNoArr.size != 2) {
            return
        }
        val orderId = orderNoArr[1].toLong()
        val order = orderService.getOne(
            QueryWrapper<Order>()
                .select(Order.ID, Order.ORDER_STATUS, Order.ACTUAL_PRICE, Order.CREATED_TIME)
                .eq(Order.ID, orderId)
        )
        val newStatus = OrderUtils.changeToWaitDelivery(order)
        if (newStatus == (-1).toByte()) {
            // 订单状态不对
            return
        }
        val oldStatus = order.orderStatus!!
        val totalAmount = yuanToFen(request.getParameter("total_amount"))
        if (totalAmount != order.actualPrice) {
            log.error(
                "orderId=[{}],payAmount=[{}],actualPrice=[{}]",
                orderId,
                totalAmount,
                order.actualPrice
            )
            return
        }
        val tradeNo = request.getParameter("trade_no")
        val tradeStatus = request.getParameter("trade_status")
        val paramMsg = StringBuilder()
        val paramMap: MutableMap<String, String?> = HashMap(request.parameterMap.size)
        for ((key, value1) in request.parameterMap) {
            var value: String? = null
            if (value1 != null) {
                value = value1[0]
            }
            paramMap[key] = value
            paramMsg.append(key).append("=").append(value)
        }
        var body: String? = null
        try {
            body = StreamUtils.copyToString(request.inputStream, StandardCharsets.UTF_8)
        } catch (e: IOException) {
            log.info("parse body failed", e)
        }
        log.warn("ali callback param=[{}],body=[{}]", paramMsg.toString(), body)
        var signVerified = false
        try {
            signVerified = AlipaySignature.rsaCheckV1(paramMap, publicKey, "UTF-8", "RSA2")
        } catch (e: AlipayApiException) {
            log.error("check signature failed", e)
        }
        if (!signVerified) {
            return
        }
        if ("TRADE_SUCCESS" != tradeStatus) {
            return
        }
        val dataModifyNum = orderService.updateStatus(orderId, newStatus, oldStatus)
        if (dataModifyNum <= 0) {
            return
        }
        if (!orderService.update(
                UpdateWrapper<Order>()
                    .set(Order.PAYMENT_TYPE, name)
                    .set(Order.PAYMENT_NO, tradeNo)
                    .eq(Order.ID, order.id)
            )
        ) {
            log.warn("orderId=[{}] update order_ext failed", orderId)
        }
    }

    override fun updateOrderStatus(order: Order?) {
        val newStatus = OrderUtils.changeToWaitDelivery(order)
        if (newStatus == (-1).toByte()) {
            // 订单状态不对
            return
        }
        val oldStatus = order!!.orderStatus!!
        val request = AlipayTradeQueryRequest()
        request.bizContent = "{" +
                "  \"out_trade_no\":\"" + getOutTradeNo(order.id) + "\"," +
                "  \"query_options\":[" +
                "    \"trade_settle_info\"" +
                "  ]" + "}"
        try {
            val response = alipayClient.execute(request)
            if (response.isSuccess && !StringUtils.isEmpty(response.body)) {
                var respJson = jsonParser.parseJsonToJsonNode(response.body)
                respJson = respJson["alipay_trade_query_response"]
                val tradeStatus = respJson["trade_status"]
                if (tradeStatus == null || "TRADE_SUCCESS" != tradeStatus.asText()) {
                    return
                }
                val totalAmount = respJson["total_amount"] ?: return
                val paymentAmount = yuanToFen(totalAmount.asText())
                if (paymentAmount != order.actualPrice) {
                    log.error(
                        "orderId=[{}],payAmount=[{}],actualPrice=[{}]",
                        order.id,
                        totalAmount,
                        order.actualPrice
                    )
                    return
                }
                val dataModifyNum = orderService.updateStatus(order.id, newStatus, oldStatus)
                if (dataModifyNum <= 0) {
                    return
                }
                order.orderStatus = newStatus
            }
        } catch (e: AlipayApiException) {
            log.error("get payment info failed", e)
        }
    }

    private fun getOutTradeNo(orderId: Long?): String {
        return "$name-$orderId"
    }
}
